/**
 * 包装 localStorage 对象，扩展存储的数据类型、缓存有效期。
 */
window.CacheStore = {
    /**
     * 垃圾回收频率 [0, 100] 0 不自动回收
     */
    autoGc: 0,
    
    get: function(key) {
        var data = window.localStorage.getItem(key);

        if (data == null) {
            return null;
        }

        data = JSON.parse(data);
        if (data.expires) {
            if (data.expires + data.cTime < this._timestamp()) {
                window.localStorage.removeItem(key);
                return null;
            }
        }

        if (this.autoGc) {
            var f = +(Math.random() * 100 ).toFixed(0);
            if (f <= this.autoGc) {
                this.gc();
            }
        }

        return data.value;
    },

    set: function(key, value, expires) {
        expires = expires || 0;
        if (+expires == NaN) {
            console.error("param 'expires' need a integer.")
            return ;
        }

        this._set(key, value, expires);
    },

    has: function(key) {
        return this.get(key) != null;
    },

    remove: function(key) {
        window.localStorage.removeItem(key);
    },

    clear: function() {
        window.localStorage.clear();
    },

    gc: function() {
        var key = "",
            data = null,
            keys = [],
            current = this._timestamp();

        for (var i = 0, l = window.localStorage.length; i < l; i ++) {
            key = window.localStorage.key(i);
            data = window.localStorage.getItem(key);
            data = JSON.parse(data);
            if (data.expires && data.expires + data.cTime < current) {
                keys.push(key);
            }
        }

        keys.map(function(k) {
            window.localStorage.removeItem(k);
        })
    },

    _ds: function() {
        return {
            value: undefined,
            cTime: 0,
            expires: 0,
        };
    },

    _set: function(key, value, expires) {
        var ds = this._ds();

        ds.value = value;
        ds.expires = expires || 0;
        ds.cTime = this._timestamp();

        window.localStorage.setItem(key, JSON.stringify(ds))
    },

    _timestamp: function() {
        return +(new Date().getTime() / 1000).toFixed(0);
    },
};

/**
 * 缓存对象
 * Useage:
 * ```javascript
 *      var cache = new LocalCache("test_");
 *      
 *      cache.set("key", {name: "Alice", age: 20}, 10); // 写缓存 "key"，有效期 10 秒
 *      var info = cache.get("key"); // 读缓存
 *      cache.has("key"); // 缓存是否存在
 *      cache.remove("key"); // 删除缓存
 *      
 *      // 读缓存，若不存在则写入
 *      cache.getOrSet("key", function() {
 *          return 100;
 *      }, 3600);
 *      
 *      //  事件订阅：
 *      // 对 key 进行 set, remove 操作时，派发订阅的事件
 *      // 若处理函数返回 false， 将不执行操作
 *      // 处理函数签名 function (type, key, value, expires) {}
 *      // - type string 事件类型："set", "remove"
 *      // - key  string 缓存key
 *      // - value mixed 缓存值，仅当 type == "set" 时存在
 *      // - expires int  缓存有效期，仅当 type == "set" 时存在
 * 
 *      cache.subscribe(id, "key", function (type, key, value, expires){
 *          
 *      })
 *      // 取消事件订阅：
 *      cache.unsubscribe(id, "key")
 *  
 *      // 清理当前前缀下的所有缓存数据
 *      // 删除所有 缓存前缀为 "test_" 的缓存数据
 *      // 警告： 若有另一缓存对象，其缓存前缀为 "test_2_"，也将被一并清理！
 *      // 若未设置缓存前缀（缓存前缀为 ""），不执行任何操作，返回 false
 *      cache.clear();
 * ``` 
 * 
 * @param {string} prefix 缓存标识前缀
 */
function LocalCache(prefix) {
    this.prefix = prefix || "";

    var _events = {};

    this.set = function(key, value, expires) {
        var events = getEvents(key);
        if (false != events) {
            for (id in events) {
                if (false != events[id].call(this, "set", key, value, expires)) {
                    return false;
                }
            }
        }
        CacheStore.set(this.prefix + key, value, expires);
        return true;
    }

    this.get = function(key) {
        return CacheStore.get(this.prefix + key);
    }

    this.getOrSet = function(key, setFunc, expires) {
        var get = this.get(key);
        if (get) {
            return get;
        }
        if (typeof setFunc != "function") {
            console.error("param 'setFunc' need a function");
            return false;
        }

        get = setFunc.call();
        this.set(key, get, expires)

        return get;
    }

    this.has = function(key) {
        return CacheStore.has(this.prefix + key);
    }

    this.remove = function(key) {
        var events = getEvents(key);
        if (false != events) {
            for (id in events) {
                if (false == events[id].call(this, "remove", key)) {
                    return false;
                }
            }
        }

        CacheStore.remove(this.prefix + key);
        return true;
    }

    this.clear = function() {
        if (this.prefix == "") {
            return false;
        }
        var length = window.localStorage.length,
            key = "",
            pLength = this.prefix.length,
            keys = [];
            
        for (var i = 0; i < length; i ++) {
            key = window.localStorage.key(i);
            if (this.prefix == key.substr(0, pLength)) {
                keys.push(key);
            }
        }

        var count = keys.length;
        keys.map(function(k) {
            window.localStorage.removeItem(k);
        })
        
        return count;
    }

    this.subscribe = function(id, key, callable) {
        if (typeof callable != "function") {
            console.error("param 'callable' need a function");
            return ;
        }
        if (! _events[key]) {
            _events[key] = {};
        }

        _events[key][id] = callable;
    }

    this.unsubscribe = function(id, key) {
        if ((! _events[key]) || (! _events[key][id])) {
            return false;
        }
        
        delete _events[key][id];
        return true;
    }

    function getEvents(key) {
        if (! _events[key]) {
            return false;
        }
        return _events[key];
    }
}
